#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>

typedef struct {
    ngx_str_t   name_servers;
    ngx_str_t   namespace;
    ngx_str_t   service_name;
    ngx_str_t   username;
    ngx_str_t   password;
    ngx_uint_t  init_server_size;
} ngx_http_upstream_nacos_srv_conf_t;

static void *ngx_http_upstream_nacos_create_srv_conf(ngx_conf_t *cf);
static char *ngx_http_upstream_nacos(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

static ngx_command_t ngx_http_upstream_nacos_commands[] = {
    {
        ngx_string("nacos"),
        NGX_HTTP_UPS_CONF | NGX_CONF_TAKE2 | NGX_CONF_TAKE3 | NGX_CONF_TAKE4,
        ngx_http_upstream_nacos,
        NGX_HTTP_SRV_CONF_OFFSET,
        0,
        NULL
    },
    ngx_null_command
};

static ngx_http_module_t ngx_http_upstream_nacos_module_ctx = {
    NULL,
    NULL,

    NULL,
    NULL,

    ngx_http_upstream_nacos_create_srv_conf,
    NULL,

    NULL,
    NULL
};

ngx_module_t ngx_http_upstream_nacos_module = {
    NGX_MODULE_V1,
    &ngx_http_upstream_nacos_module_ctx,
    ngx_http_upstream_nacos_commands,
    NGX_HTTP_MODULE,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NGX_MODULE_V1_PADDING
};

static void *ngx_http_upstream_nacos_create_srv_conf(ngx_conf_t *cf) {
    ngx_http_upstream_nacos_srv_conf_t  *conf;
    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_nacos_srv_conf_t));
    if (conf == NULL) {
        return NULL;
    }

    conf->init_server_size = 4;
    return conf;
}

static char *ngx_http_upstream_nacos(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {
    ngx_http_upstream_srv_conf_t  *uscf;
    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
    ngx_log_error(NGX_LOG_NOTICE, cf->log, 0, "ups srv host: %V", &uscf->host);

    ngx_array_t *srvs = uscf->servers;
    ngx_http_upstream_server_t *srv = srvs->elts;
    srvs->nelts = 0;

    ngx_http_upstream_server_t  *us;
    ngx_url_t u;
    us = ngx_array_push(uscf->servers);
    ngx_memzero(us, sizeof(ngx_http_upstream_server_t));
    ngx_memzero(&u, sizeof(ngx_url_t));
    ngx_str_set(&u.url, "192.168.50.42:23789");
    u.default_port = 80;
    ngx_parse_url(cf->pool, &u);
    us->name = u.url;
    us->addrs = u.addrs;
    us->naddrs = u.naddrs;
    us->weight = 1;
    us->max_conns = 0;
    us->max_fails = 1;
    us->fail_timeout = 10;

    us = ngx_array_push(uscf->servers);
    ngx_memzero(us, sizeof(ngx_http_upstream_server_t));
    ngx_memzero(&u, sizeof(ngx_url_t));
    ngx_str_set(&u.url, "192.168.50.43:23789");
    u.default_port = 80;
    ngx_parse_url(cf->pool, &u);
    us->name = u.url;
    us->addrs = u.addrs;
    us->naddrs = u.naddrs;
    us->weight = 1;
    us->max_conns = 0;
    us->max_fails = 1;
    us->fail_timeout = 10;

    us = ngx_array_push(uscf->servers);
    ngx_memzero(us, sizeof(ngx_http_upstream_server_t));
    ngx_memzero(&u, sizeof(ngx_url_t));
    ngx_str_set(&u.url, "192.168.50.44:23789");
    u.default_port = 80;
    ngx_parse_url(cf->pool, &u);
    us->name = u.url;
    us->addrs = u.addrs;
    us->naddrs = u.naddrs;
    us->weight = 1;
    us->max_conns = 0;
    us->max_fails = 1;
    us->fail_timeout = 10;

    for (ngx_uint_t i = 0; i < srvs->nelts; ++i) {
        ngx_http_upstream_server_t s = srv[i];
        ngx_log_error(NGX_LOG_NOTICE, cf->log, 0, "server info: %V", &s.name);
    }

    ngx_http_upstream_nacos_srv_conf_t *ncf = conf;

    u_char *mark;
    ngx_uint_t i;
    ngx_int_t init_server_size;

    ngx_str_t *value = cf->args->elts;

    ncf->name_servers = value[1];

    ngx_str_t namespace_service = value[2];
    mark = (u_char *) ngx_strchr(namespace_service.data, ' ');
    ngx_uint_t namespace_len = mark - namespace_service.data;
    ngx_uint_t service_len = namespace_service.len - namespace_len - 1;

    ncf->namespace.len = namespace_len;
    ncf->namespace.data = namespace_service.data;

    ncf->service_name.len = service_len;
    ncf->service_name.data = mark + 1;

    for (i = 3; i < cf->args->nelts; ++i) {
        ngx_str_t arg = value[i];
        if (ngx_strncmp(arg.data, "username_pwd=", 13) == 0) {
            u_char *username_pwd = arg.data + 13;
            u_char *mk = (u_char *) ngx_strchr(username_pwd, ' ');
            ngx_uint_t username_len = mk - username_pwd;
            ngx_uint_t pwd_len = arg.len - 13 - username_len - 1;

            ncf->username.len = username_len;
            ncf->username.data = username_pwd;

            ncf->password.len = pwd_len;
            ncf->password.data = mk + 1;
            continue;
        }

        if (ngx_strncmp(arg.data, "init_server_size=", 17) == 0) {
            init_server_size = ngx_atoi(&arg.data[17], arg.len - 17);
            if (init_server_size == NGX_ERROR) {
                goto invalid;
            }
            ncf->init_server_size = init_server_size;
            continue;
        }

        goto invalid;
    }

    ngx_log_error(NGX_LOG_NOTICE, cf->log, 0, "name servers is: %V, namespace is: %V, service is: %V, username is: %V, password is: %V, init server size is: %d",
                  &ncf->name_servers, &ncf->namespace, &ncf->service_name, &ncf->username, &ncf->password, ncf->init_server_size);

    return NGX_CONF_OK;

invalid:
    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"", &value[i]);
    return NGX_CONF_ERROR;
}